%%%-------------------------------------------------------------------
%%% File    : mapper_node_srv.erl
%%% Author  :  vjache
%%% Description : State machine library module.
%%%
%%% Created : 21 Jan 2010 by  vjache
%%%-------------------------------------------------------------------
-module(sm).

%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include_lib("eunit/include/eunit.hrl").
-include("../../include/sm.hrl").
-include("../../include/pipe.hrl").
-include("../../include/iter.hrl").

%% --------------------------------------------------------------------
%% Exported Functions
%% --------------------------------------------------------------------
-export([new/1,
		 new/2,
		 chain/1,
		 chain/2,
		 put/2,
		 map/2,
		 buffer/1,
		 counter/1,
		 filter/1,
		 mapper/1,
		 list/1,
		 pipe_wrapper/1,
		 iter_wrapper/1]).

%% --------------------------------------------------------------------
%% API Functions
%% --------------------------------------------------------------------
new(State0,TFun) when is_function(TFun, 2) ->
	#sm{state=State0,tfun=TFun}.

new(TFun) when is_function(TFun, 1) ->
	#sm{state=0,
		tfun=fun(0,Input)-> 
					 case TFun(Input) of
						 {out,O} ->
							 {out,0,O};
						 noout ->
							 {noout,0}
					 end
			 end}.

put(Sm=#sm{state=State0,tfun=TFun}, Input) ->
	case TFun(State0,Input) of
		{out,State1,Output} ->
			{out,Sm#sm{state=State1},Output};
		{noout,State1} ->
			{noout,Sm#sm{state=State1}}
	end.

map(Sm, InputList) when is_list(InputList),is_record(Sm, sm) ->
	map(Sm, InputList,[]).
map(Sm, [],OutputList) ->
	{Sm,lists:reverse(OutputList)};
map(Sm=#sm{state=S,tfun=TFun}, [I|T],OutputList) ->
	case TFun(S,I) of
		{out,S1,O} ->
			map(Sm#sm{state=S1}, T,[{out,O}|OutputList]);
		{noout,S1} ->
			map(Sm#sm{state=S1}, T,[noout|OutputList])
	end.


chain(SmList) when is_list(SmList) ->
	new(SmList, 
		fun(S,I)-> 
				chain_transition(S,I,undefined)
		end).

chain(SmList,RepairFun) when is_list(SmList),is_function(RepairFun, 3) ->
	new(SmList, 
		fun(S,I)-> 
				chain_transition(S,I,RepairFun)
		end).

chain_transition(SmList,Input,RepairFun) ->
	chain_transition(SmList,Input,[],RepairFun).
chain_transition(_SmList=[],Input,NewSmList,_RepairFun) ->
	{out,lists:reverse(NewSmList),Input};
chain_transition([Sm=#sm{state=S,tfun=F}|T],Input,NewSmList,RepairFun) ->
	try F(S,Input) of
		{out,S1,O} ->
			chain_transition(T,O,[Sm#sm{state=S1}|NewSmList],RepairFun);
		{noout,S1} ->
			{noout,lists:reverse(NewSmList)++[Sm#sm{state=S1}|T]}
	catch
		_:Reason when RepairFun==undefined ->
			throw({submachine_failure,length(NewSmList)+1,Reason});
		_:Reason ->
			Sm1=RepairFun(length(NewSmList)+1,Reason,Sm),
			chain_transition([Sm1|T],Input,NewSmList,RepairFun)
	end.
list(List) when is_list(List)->
	new(List,
		fun([O|T],_)->
				{out,T,O};
		   ([],_)->
				{noout,[]}
		end).
mapper(MapFun) when is_function(MapFun,1)->
	new(0,fun(0,Input)->
				  {out,0,MapFun(Input)}
		end).
filter(PredFun) when is_function(PredFun, 1) ->
	new(0,fun(0,Input)->
				  case PredFun(Input) of
					  true ->
						  {out,0,Input};
					  false ->
						  {noout,0}
				  end
		end).
counter(Start) when is_integer(Start) ->
	new(Start,fun(S,Input)->
					  {out,S+1,{S,Input}}
		end).
buffer(BuffSize) when is_integer(BuffSize),BuffSize>0 ->
	new({0,[]},
		fun({Size,Buff},Input) when Size<BuffSize ->
				{noout,{Size+1,[Input|Buff]}};
		   ({_,Buff},Input) ->
				{out,{1,[Input]},lists:reverse(Buff)}
		end).
pipe_wrapper(Pipe) when ?IS_PIPE(Pipe) ->
	new(open,
		fun(open,Input)->
				PipeState=pipe:open(Pipe),
				{noout,pipe:push(PipeState,Input)};
		   (PipeState,Input)->
				{noout,pipe:push(PipeState,Input)}
		end).
iter_wrapper(Iter) when ?IS_ITER(Iter) ->
	new(open,
		fun(open,_)->
				IterState=iter:open(Iter),
				{IterState1,Output}=iter:next(IterState),
				{out,IterState1,Output};
		   (IterState,_)->
				{IterState1,Output}=iter:next(IterState),
				case Output of
					?EOF ->
						{noout,IterState1};
					_ ->
						{out,IterState1,Output}
				end
		end).

%% --------------------------------------------------------------------
%% Local Functions
%% --------------------------------------------------------------------
basic_test()->
	Sm=chain([filter(fun(I)->I>5 end),
			  mapper(fun(I)->{m,I} end),
			  buffer(3),
			  mapper(fun(I)-> zlib:zip(term_to_binary(I)) end)]),
	map(Sm, lists:seq(1, 100)).
